{***************************************************************************}
{                                                                           }
{           Spring Framework for Delphi                                     }
{                                                                           }
{           Copyright (c) 2009-2013 Spring4D Team                           }
{                                                                           }
{           http://www.spring4d.org                                         }
{                                                                           }
{***************************************************************************}
{                                                                           }
{  Licensed under the Apache License, Version 2.0 (the "License");          }
{  you may not use this file except in compliance with the License.         }
{  You may obtain a copy of the License at                                  }
{                                                                           }
{      http://www.apache.org/licenses/LICENSE-2.0                           }
{                                                                           }
{  Unless required by applicable law or agreed to in writing, software      }
{  distributed under the License is distributed on an "AS IS" BASIS,        }
{  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. }
{  See the License for the specific language governing permissions and      }
{  limitations under the License.                                           }
{                                                                           }
{***************************************************************************}

unit Spring.Collections.Sets;

interface

{$I Spring.inc}

uses
  Generics.Collections,
  Generics.Defaults,
  Spring,
  Spring.Collections,
  Spring.Collections.Base;

type
  THashSet<T> = class(TCollectionBase<T>, ISet<T>)
  protected
    type
      TGenericDictionary = Generics.Collections.TDictionary<T, Integer>;
  private
    fDictionary: TGenericDictionary; // TEMP Impl
    procedure KeyNotify(Sender: TObject; const Item: T; Action: TCollectionNotification);
  protected
    function GetCount: Integer; override;
  public
    constructor Create; overload; override;
    constructor Create(const comparer: IEqualityComparer<T>); overload;
    constructor Create(const collection: array of T; const comparer: IEqualityComparer<T>); overload;
    constructor Create(const collection: IEnumerable<T>; const comparer: IEqualityComparer<T>); overload;

    destructor Destroy; override;

    function GetEnumerator: IEnumerator<T>; override;

    function Add(const item: T): Boolean; override;
    function Remove(const item: T): Boolean; override;
    function Extract(const item: T): T; override;

    procedure Clear; override;

    procedure ExceptWith(const collection: IEnumerable<T>);
    procedure IntersectWith(const collection: IEnumerable<T>);
    procedure UnionWith(const collection: IEnumerable<T>);

    function Contains(const item: T): Boolean; override;
    function Overlaps(const collection: IEnumerable<T>): Boolean;
    function SetEquals(const collection: IEnumerable<T>): Boolean;
  end;

implementation

uses
  Spring.Collections.Lists,
  Spring.Collections.Extensions;


{$REGION 'THashSet<T>'}

constructor THashSet<T>.Create;
begin
  inherited Create;
  fDictionary := TGenericDictionary.Create;
  fDictionary.OnKeyNotify := KeyNotify;
end;

constructor THashSet<T>.Create(const comparer: IEqualityComparer<T>);
begin
  inherited Create;
  fDictionary := TGenericDictionary.Create(comparer);
end;

constructor THashSet<T>.Create(const collection: array of T;
  const comparer: IEqualityComparer<T>);
begin
  inherited Create;
  fDictionary := TGenericDictionary.Create(comparer);
  AddRange(collection);
end;

constructor THashSet<T>.Create(const collection: IEnumerable<T>;
  const comparer: IEqualityComparer<T>);
begin
  inherited Create;
  fDictionary := TGenericDictionary.Create(comparer);
  AddRange(collection);
end;

destructor THashSet<T>.Destroy;
begin
  fDictionary.Free;
  inherited Destroy;
end;

function THashSet<T>.Add(const item: T): Boolean;
begin
  Result := not fDictionary.ContainsKey(item);
  if Result then
    fDictionary.Add(item, 0);
end;

procedure THashSet<T>.Clear;
begin
  fDictionary.Clear;
end;

function THashSet<T>.Contains(const item: T): Boolean;
begin
  Result := fDictionary.ContainsKey(item);
end;

procedure THashSet<T>.ExceptWith(const collection: IEnumerable<T>);
var
  item: T;
begin
//  TArgument.CheckNotNull(collection <> nil, 'collection');

  if collection.AsObject = Self then
  begin
    Clear;
  end
  else
  begin
    for item in collection do
    begin
      Remove(item);
    end;
  end;
end;

function THashSet<T>.Extract(const item: T): T;
begin
  if fDictionary.ContainsKey(item) then
  begin
    Result := fDictionary.ExtractPair(item).Key;
  end
  else
  begin
    Result := Default(T);
  end;
end;

function THashSet<T>.GetCount: Integer;
begin
  Result := fDictionary.Count;
end;

function THashSet<T>.GetEnumerator: IEnumerator<T>;
begin
  Result := TEnumeratorAdapter<T>.Create(fDictionary.Keys);
end;

procedure THashSet<T>.IntersectWith(const collection: IEnumerable<T>);
var
  item: T;
  list: IList<T>;
begin
//  TArgument.CheckNotNull(collection <> nil, 'collection');

  if collection.Count = 0 then
  begin
    Clear;
  end
  else
  begin
    list := TList<T>.Create;
    for item in Self do
    begin
      if not collection.Contains(item) then
        list.Add(item);
    end;

    for item in list do
    begin
      Remove(item);
    end;
  end;
end;

procedure THashSet<T>.KeyNotify(Sender: TObject; const Item: T;
  Action: TCollectionNotification);
begin
  DoChanged(item, TCollectionChangedAction(Action));
end;

function THashSet<T>.Overlaps(const collection: IEnumerable<T>): Boolean;
var
  item: T;
begin
//  TArgument.CheckNotNull(collection <> nil, 'collection');

  if Count = 0 then
    Exit(False);

  for item in collection do
  begin
    if Contains(item) then
      Exit(True);
  end;
  Result := False;
end;

function THashSet<T>.Remove(const item: T): Boolean;
begin
  Result := fDictionary.ContainsKey(item);
  if Result then
    fDictionary.Remove(item);
end;

function THashSet<T>.SetEquals(const collection: IEnumerable<T>): Boolean;
var
  item: T;
  localSet: ISet<T>;
begin
//  TArgument.CheckNotNull(collection <> nil, 'collection');

  localSet := THashSet<T>.Create;

  for item in collection do
  begin
    if not Contains(item) then
      Exit(False);
    localSet.Add(item);
  end;

  for item in Self do
  begin
    if not localSet.Contains(item) then
      Exit(False);
  end;

  Result := True;
end;

procedure THashSet<T>.UnionWith(const collection: IEnumerable<T>);
var
  item: T;
begin
//  TArgument.CheckNotNull(collection <> nil, 'collection');

  for item in collection do
  begin
    Add(item);
  end;
end;

{$ENDREGION}


end.
